Skip to content

Conversation

EmilyyyLiu
Copy link
Contributor

@EmilyyyLiu EmilyyyLiu commented Sep 8, 2025

关联issue:ant-design/ant-design#54854
替换 useMergedState 为 useControlledState

Summary by CodeRabbit

  • 新特性

  • 重构

    • 将选择组件的内部状态管理由合并状态切换为受控/非受控模式,覆盖展开状态、搜索值与内部值;对外行为和公共 API 不变。
  • 杂务

    • 更新依赖 @rc-component/util 至 ^1.3.0。

Copy link

vercel bot commented Sep 8, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
select Ready Ready Preview Comment Sep 8, 2025 8:59am

Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

将组件内部若干受控/非受控状态管理钩子从 useMergedState 切换为 useControlledState;同时将依赖 @rc-component/util 版本从 ^1.2.1 升级到 ^1.3.0。无导出 API 改动,控制流保持一致。

Changes

Cohort / File(s) Summary
依赖更新
package.json
@rc-component/util^1.2.1 升级到 ^1.3.0
BaseSelect 打开态状态钩子替换
src/BaseSelect/index.tsx
useMergedStateuseControlledStateinnerOpen 初始化由 useMergedState<boolean>(false, { defaultValue: defaultOpen, value: open }) 改为 `useControlledState(defaultOpen
Select 搜索与内部值状态钩子替换
src/Select.tsx
将搜索值与内部值的 useMergedState 替换为 useControlledState:搜索由 useMergedState('', { value: searchValue, postState: ... }) 改为 useControlledState('', searchValue) 并以 `internalSearchValue

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as 用户
  participant BaseSelect as BaseSelect
  participant OpenHook as useControlledState
  Note over BaseSelect,OpenHook: 展开态支持受控(open)与非受控(defaultOpen)两种模式
  User->>BaseSelect: 点击打开/关闭
  BaseSelect->>OpenHook: setInnerOpen(next)
  alt open 由外部传入(受控)
    Note over BaseSelect: 渲染受 props.open 驱动的状态
  else 非受控
    Note over BaseSelect: 由内部 state 更新并渲染
  end
  BaseSelect->>User: 更新下拉可见性(mergedOpen)
Loading
sequenceDiagram
  autonumber
  actor User as 用户
  participant Select as Select
  participant SearchHook as useControlledState
  participant ValueHook as useControlledState
  Note over Select: 搜索值与内部值均支持受控/非受控模式
  User->>Select: 输入搜索或选择项
  Select->>SearchHook: setSearchValue(input)
  Select->>ValueHook: setInternalValue(selection)
  alt props 提供值(受控)
    Note over Select: 使用 props.searchValue / props.value 驱动展示
  else 非受控
    Note over Select: 使用内部 state 驱动展示
  end
  Select->>User: 更新 UI 与选择结果
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • zombieJ

Poem

小兔轻敲键与钩,
状态更替不慌张。
受控与否任君断,
搜索选中两相帮。
版本微升静无声,🥕


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3acb0fb and 0796710.

📒 Files selected for processing (1)
  • src/Select.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Select.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @EmilyyyLiu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在通过升级核心工具库并替换旧的状态管理钩子,来改进项目中的状态处理机制。这一系列更改有助于提高代码的可维护性和一致性,确保组件能够更有效地响应外部控制和内部状态变化。

Highlights

  • @rc-component/util 依赖升级: 将 @rc-component/util 库的版本从 ^1.2.1 升级到 ^1.3.0
  • 替换 useMergedState 为 useControlledState: 在 src/BaseSelect/index.tsxsrc/Select.tsx 文件中,将 useMergedState 钩子替换为新的 useControlledState 钩子,以优化组件内部状态的管理方式。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request upgrades the @rc-component/util dependency and replaces useMergedState with useControlledState in BaseSelect and Select components. The changes aim to improve state management and maintain component functionality. I have identified some areas for improvement, mainly focusing on ensuring the correct usage of useControlledState and dependency updates.

import cls from 'classnames';
import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect';
import useMergedState from '@rc-component/util/lib/hooks/useMergedState';
import useControlledState from '@rc-component/util/lib/hooks/useControlledState';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Replacing useMergedState with useControlledState is the core of this refactor. Ensure that the behavior of useControlledState aligns with the previous behavior of useMergedState, especially regarding default values and controlled/uncontrolled component logic.

"@rc-component/trigger": "^3.0.0",
"@rc-component/motion": "^1.1.4",
"@rc-component/util": "^1.2.1",
"@rc-component/util": "^1.3.0",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This is good to upgrade to the latest version of @rc-component/util. However, ensure that all the changes in the new version are compatible with the current implementation and that all functionalities are working as expected.

defaultValue: defaultOpen,
value: open,
});
const [innerOpen, setInnerOpen] = useControlledState<boolean>(defaultOpen || false, open);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The useControlledState hook is used here. It's important to verify that the initial state and the controlled value are correctly handled. Consider adding a comment explaining why defaultOpen || false is used as the initial state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在原本 useMergedState 的使用中 defaultOpen 的使用优先于默认的 false

src/Select.tsx Outdated
value: searchValue,
postState: (search) => search || '',
});
const [mergedSearchValue, setSearchValue] = useControlledState('', searchValue);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Verify that the initial state and controlled value are correctly handled for mergedSearchValue. Ensure that the component behaves as expected when the searchValue prop changes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里要额外写一下:

const [internalSearchValue, setSearchValue] = useControlledState('', searchValue);
const mergedSearchValue = internalSearchValue || '';

Copy link

codecov bot commented Sep 8, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.11%. Comparing base (143e4d2) to head (0796710).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1162   +/-   ##
=======================================
  Coverage   98.11%   98.11%           
=======================================
  Files          39       39           
  Lines        1489     1489           
  Branches      423      425    +2     
=======================================
  Hits         1461     1461           
  Misses         28       28           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/BaseSelect/index.tsx (1)

386-386: 行为差异提醒:useMergedState → useControlledState 可能限制“受控/非受控切换”

  • useControlledState 通常在首渲染锁定“是否受控”,后续不鼓励从非受控切换为受控(或反之)。若外部对 open 存在动态切换(从 undefined 变为 boolean 或相反),可能与旧实现的宽松行为不同,产生不可预期状态。
  • 当前初始化 defaultOpen || false 语义与旧逻辑等价,OK。

为降低误用风险,可在开发环境做一次警告封装(仅示意):

-const [innerOpen, setInnerOpen] = useControlledState<boolean>(defaultOpen || false, open);
+const [innerOpen, setInnerOpen] = useControlledState<boolean>(defaultOpen || false, open);
+if (process.env.NODE_ENV !== 'production') {
+  const controlledRef = React.useRef(open !== undefined);
+  React.useEffect(() => {
+    if (controlledRef.current !== (open !== undefined)) {
+      // eslint-disable-next-line no-console
+      console.warn('[BaseSelect] `open` 从受控切换到非受控(或相反)不被支持,请固定用法。');
+    }
+  }, [open]);
+}

另:微建议将 defaultOpen || false 改为更直观的 Boolean(defaultOpen)

请补一例用例覆盖:

  • 仅传 defaultOpen(非受控);
  • open(受控);
  • 模拟从未传 open → 传入 open 的切换(应得到预期警告/行为)。
src/Select.tsx (1)

275-276: 移除 postState 后的空值归一化差异:请确认不会传入 null

原先 useMergedState('', { value: searchValue, postState: v => v || '' }) 会把 undefined/null 归一为 ''。现在 useControlledState('', searchValue) 若外部误传 nullmergedSearchValue 可能为 null,进而影响字符串操作与过滤逻辑。

若希望保持兼容,可在取值处做一次兜底归一(不改变受控判定):

-const [mergedSearchValue, setSearchValue] = useControlledState('', searchValue);
+const normalizedSearchValue = (searchValue as string | undefined) ?? '';
+const [mergedSearchValue, setSearchValue] = useControlledState('', normalizedSearchValue);

或者在使用点统一 (mergedSearchValue || '')。请结合现有用例选择其一,并补充测试覆盖 searchValue: undefined/null/''

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 143e4d2 and 3acb0fb.

📒 Files selected for processing (3)
  • package.json (1 hunks)
  • src/BaseSelect/index.tsx (2 hunks)
  • src/Select.tsx (3 hunks)
🔇 Additional comments (4)
package.json (1)

54-54: 校验 util v1.3.0 导出行为与用法一致

  • 确认项目依赖解析的 @rc-component/util 版本 ≥ 1.3.0(若仓库未包含锁文件,请根据 package.json 或安装工具手动检查)。
  • 检查 node_modules/@rc-component/util/lib/hooks/useControlledState 是否为 default export,且其签名与在 src/Select.tsx(第 32 行)和 src/BaseSelect/index.tsx(第 4 行)中的用法保持一致。
  • 发布包在 ESM/CJS 导出上可能存在差异,打包后务必验证运行时兼容性。
src/BaseSelect/index.tsx (1)

4-4: 改为使用 useControlledState:导入路径与默认导出需与 [email protected] 对齐

导入改为默认导出符合 [email protected] 的“default export”调整,但仍建议编译验证以防导出名与形态不一致导致运行时错误。(github.com)

src/Select.tsx (2)

32-32: 改为 useControlledState:导入层面无问题,但请确保 util 导出兼容

与 BaseSelect 同步注意点:确认默认导出与签名一致,避免构建期/运行期异常。


343-344: 值状态迁移为 useControlledState:语义对齐,注意受控模式下 setInternalValue 为 no-op

此改动与组件受控/非受控设计契合;在受控模式下 setInternalValue 将 no-op,后续变更由外部 value 驱动,符合预期。

建议新增/回归测试覆盖:

  • multiple 模式下 value=null[] 的显示行为(你已在后续用 internalValue===null ? [] : internalValue 兜底,验证一下可避免回归)。

@EmilyyyLiu EmilyyyLiu changed the title refactor: Upgrade utils and replace useMergedState [WIP]refactor: Upgrade utils and replace useMergedState Sep 8, 2025
@EmilyyyLiu EmilyyyLiu changed the title [WIP]refactor: Upgrade utils and replace useMergedState refactor: Upgrade utils and replace useMergedState Sep 8, 2025
@zombieJ zombieJ merged commit 3cc9d0f into react-component:master Sep 9, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants